﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Settings.cs" company="Geologische Bundesanstalt">
//   Thomas Heuberger, 2014-02
// </copyright>
// <summary>
//   Defines the Settings type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace AttachmentHandler {
    using System;
    using System.ComponentModel;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.CompilerServices;

    using AttachmentHandler.Annotations;

    using Outlook = Microsoft.Office.Interop.Outlook;

    /// <summary>
    /// The settings class.
    /// </summary>
    public class Settings : INotifyPropertyChanged, IDataErrorInfo {
        /// <summary>
        /// The source folder.
        /// </summary>
        private Outlook.Folder sourceFolder;

        /// <summary>
        /// The destination folder.
        /// </summary>
        private string destinationFolder;

        /// <summary>
        /// The only unread field.
        /// </summary>
        private bool onlyUnread;

        /// <summary>
        /// The mark as read field.
        /// </summary>
        private bool markAsRead;

        /// <summary>
        /// Initializes a new instance of the <see cref="Settings"/> class.
        /// </summary>
        public Settings() {
            this.OnlyUnread = Properties.Settings.Default.OnlyUnread;
            this.MarkAsRead = Properties.Settings.Default.MarkAsRead;
            this.DestinationFolder = Properties.Settings.Default.DestinationFolder;

            // if EntryID or StoreID are null, return early ...
            if (string.IsNullOrEmpty(Properties.Settings.Default.EntryID) || string.IsNullOrEmpty(Properties.Settings.Default.StoreID))
                return;
            
            try {
                // ... if not try to get the folder from EntryID and StoreID
                this.SourceFolder = Globals.ThisAddIn.Application.Session.GetFolderFromID(Properties.Settings.Default.EntryID, Properties.Settings.Default.StoreID) as Outlook.Folder;
            } 
                // ReSharper disable once EmptyGeneralCatchClause
            catch (Exception) {
                // suppress any errors. there will be a null check later anyways
            }
        }

        /// <summary>
        /// The property changed.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets a value indicating whether the object itself is valid.
        /// </summary>
        public bool IsValid {
            get {
                return this.ValidateSettings();
            }
        }

        /// <summary>
        /// Gets the error.
        /// </summary>
        public string Error {
            get {
                return this[null];
            }
        }

        /// <summary>
        /// Gets or sets the source folder.
        /// </summary>
        public Outlook.Folder SourceFolder {
            get {
                return this.sourceFolder;
            }

            set {
                if (object.Equals(value, this.sourceFolder)) return;
                this.sourceFolder = value;

                // update the backend
                Properties.Settings.Default.StoreID = value == null ? string.Empty : value.StoreID;
                Properties.Settings.Default.EntryID = value == null ? string.Empty : value.EntryID;

                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets the destination folder.
        /// </summary>
        public string DestinationFolder {
            get {
                return this.destinationFolder;
            }

            set {
                if (value == this.destinationFolder) return;
                this.destinationFolder = value;

                // update the backend
                Properties.Settings.Default.DestinationFolder = value;

                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether only unread should be processed.
        /// </summary>
        public bool OnlyUnread {
            get {
                return this.onlyUnread;
            }

            set {
                if (value.Equals(this.onlyUnread)) return;
                this.onlyUnread = value;
                
                // update the backend
                Properties.Settings.Default.OnlyUnread = value;
                
                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether mails should be marked as read after processing.
        /// </summary>
        public bool MarkAsRead {
            get {
                return this.markAsRead;
            }

            set {
                if (value.Equals(this.markAsRead)) return;
                this.markAsRead = value;

                // update the backend
                Properties.Settings.Default.MarkAsRead = value;

                this.OnPropertyChanged();
            }
        }

        /// <summary>
        /// The indexer used for error checking.
        /// </summary>
        /// <param name="columnName">
        /// The column name.
        /// </param>
        /// <returns>
        /// The <see cref="string"/>.
        /// </returns>
        public string this[string columnName] {
            get {
                switch (columnName) {
                    case "SourceFolder":
                        if (this.sourceFolder == null)
                            return "The source folder must be a valid folder of your mailbox! Please check your folder!";
                        break;
                    case "DestinationFolder":
                        if (!this.CheckDestinationDirectory())
                            return "The destination folder must exist and must be writeable! Please check your folder!";
                        break;
                    default:
                        return string.Empty;
                }

                return string.Empty;
            }
        }

        /// <summary>
        /// The set to factory default method. only used for development
        /// </summary>
        public void RestoreDefault() {
                this.SourceFolder = null;
                this.DestinationFolder = string.Empty;

                this.OnlyUnread = true;
                this.MarkAsRead = true;

                this.Save();
        }

        /// <summary>
        /// The save settings method.
        /// </summary>
        public void Save() {
            // persist the changes to the settings file
            Properties.Settings.Default.Save();
        }

        /// <summary>
        /// The on property changed.
        /// </summary>
        /// <param name="propertyName">
        /// The property name.
        /// </param>
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
            var handler = this.PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }   

        /// <summary>
        /// The validate settings method.
        /// </summary>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool ValidateSettings() {
            return this.CheckDestinationDirectory() && this.SourceFolder != null;
        }

        /// <summary>
        /// Checks for write permissions to a given directory.
        /// </summary>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool CheckWritePermission() {
            try {
                // Attempt to get a list of security permissions from the folder. 
                // This will raise an exception if the path is read only or do not have access to view the permissions. 
                using (
                    var fs = File.Create(Path.Combine(this.destinationFolder, Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose)) {
                }

                return true;
            } catch (Exception) {
                return false;
            }
        }

        /// <summary>
        /// Checks if the destination directory exists and if it's writeable.
        /// </summary>
        /// <returns>
        /// The <see cref="bool"/>.
        /// </returns>
        private bool CheckDestinationDirectory() {
            if (string.IsNullOrEmpty(this.destinationFolder)) return false;

            if (!Directory.Exists(this.destinationFolder) || !this.CheckWritePermission()) return false;
            return true;
        }     
    }
}
